package org.Yiran.timetale_re.block.base;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.Yiran.timetale_re.util.geometry.ModelJsonVoxelShapeParser;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

/**
 * 多方块结构基类
 * <p>
 * 这个抽象类为创建由多个部分组成的复杂方块结构提供了基础框架，
 * 如长桌、大型机器等。它处理了多方块结构的放置、验证、移除等逻辑。
 */
public abstract class MultiBlockBase extends HorizontalDirectionalBlock {
    // 面向属性，定义方块的朝向
    public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;

    // 存储每个部分的模型路径
    protected final Map<Integer, ModelJsonVoxelShapeParser.CachedShapeGenerator> shapeGenerators = new HashMap<>();
    protected final int totalParts;

    /**
     * 构造函数
     * 
     * @param properties 方块属性
     * @param modId 模组ID
     * @param blockName 方块名称
     * @param totalParts 总部分数
     * @param modelPathGenerator 模型路径生成器函数
     */
    public MultiBlockBase(Properties properties, String modId, String blockName, int totalParts, BiFunction<String, Integer, String> modelPathGenerator) {
        super(properties);
        this.totalParts = totalParts;

        // 为每个部分创建形状生成器
        for (int i = 0; i < totalParts; i++) {
            String modelPath = modelPathGenerator.apply(blockName, i);
            shapeGenerators.put(i, new ModelJsonVoxelShapeParser.CachedShapeGenerator(modId, modelPath, blockName));
        }

        this.registerDefaultState(this.stateDefinition.any()
                .setValue(FACING, Direction.NORTH)
                .setValue(getPartIdProperty(), 0));
    }

    // ==================== 抽象方法 ====================

    /**
     * 获取部分ID属性
     * 子类需要提供一个IntegerProperty来标识不同的部分
     * 
     * @return 部分ID属性
     */
    public abstract IntegerProperty getPartIdProperty();

    /**
     * 放置其他部分
     * 当主部分被放置时，这个方法负责放置结构的其他部分
     * 
     * @param level 世界对象
     * @param basePos 基础位置
     * @param facing 朝向
     * @param mainPartId 主部分ID
     */
    protected abstract void placeOtherParts(Level level, BlockPos basePos, Direction facing, int mainPartId);

    /**
     * 检查是否可以放置
     * 验证在指定位置和朝向是否可以放置整个多方块结构
     * 
     * @param level 世界对象
     * @param pos 放置位置
     * @param facing 朝向
     * @return 是否可以放置
     */
    protected abstract boolean canPlaceAt(Level level, BlockPos pos, Direction facing);

    /**
     * 验证部分是否有效
     * 检查多方块结构的各个部分是否仍然有效连接
     * 
     * @param level 世界对象
     * @param pos 部分位置
     * @param facing 朝向
     * @param partId 部分ID
     * @return 部分是否有效
     */
    protected abstract boolean isPartValid(LevelReader level, BlockPos pos, Direction facing, int partId);

    /**
     * 移除时掉落物品
     * 当多方块结构被移除时，处理物品掉落逻辑
     * 
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     */
    protected abstract void dropItemsOnRemove(BlockState state, Level level, BlockPos pos);

    /**
     * 移除其他部分
     * 当主部分被移除时，清理结构的其他部分
     * 
     * @param level 世界对象
     * @param pos 位置
     * @param facing 朝向
     * @param partId 部分ID
     */
    protected abstract void removeOtherParts(Level level, BlockPos pos, Direction facing, int partId);

    // ==================== 状态定义 ====================

    /**
     * 创建方块状态定义
     * 
     * @param builder 状态定义构建器
     */
    @Override
    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
        builder.add(FACING, getPartIdProperty());
    }

    // ==================== 物理属性 ====================

    /**
     * 获取活塞推动反应
     * 多方块结构通常不能被活塞推动
     * 
     * @param state 方块状态
     * @return 推动反应类型
     */
    @Override
    public PushReaction getPistonPushReaction(BlockState state) {
        return PushReaction.BLOCK;
    }

    // ==================== 放置逻辑 ====================

    /**
     * 获取放置时的方块状态
     * 
     * @param context 放置上下文
     * @return 放置时的方块状态
     */
    @Override
    public BlockState getStateForPlacement(BlockPlaceContext context) {
        Direction facing = context.getHorizontalDirection();
        BlockPos basePos = context.getClickedPos();
        Level level = context.getLevel();

        // 先检查是否有实体阻挡
        if (hasEntityObstruction(level, basePos, facing)) {
            return null;
        }

        // 检查是否有足够空间放置所有部分
        if (canPlaceAt(level, basePos, facing)) {
            // 在客户端进行预测性放置，减少视觉延迟
            if (level.isClientSide) {
                // 在客户端预放置其他部分以提供即时视觉反馈
                prePlaceVisual(level, basePos, facing);
            }
            return this.defaultBlockState()
                    .setValue(FACING, facing)
                    .setValue(getPartIdProperty(), 0);
        }

        return null;
    }

    /**
     * 检查是否有实体阻挡放置
     * 
     * @param level 世界对象
     * @param pos 放置位置
     * @param facing 朝向
     * @return 是否有实体阻挡
     */
    protected boolean hasEntityObstruction(Level level, BlockPos pos, Direction facing) {
        for (int i = 0; i < totalParts; i++) {
            BlockPos partPos = getPartPosition(pos, facing, i);
            if (partPos != null) {
                List<Entity> entities = level.getEntities(null, new AABB(partPos));
                // 排除掉落物实体
                for (Entity entity : entities) {
                    if (!(entity instanceof ItemEntity)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 在客户端预放置视觉效果
     * 
     * @param level 世界对象
     * @param basePos 基础位置
     * @param facing 朝向
     */
    protected void prePlaceVisual(Level level, BlockPos basePos, Direction facing) {
        if (level.isClientSide) {
            // 预放置所有部分以提供即时视觉反馈
            for (int i = 1; i < totalParts; i++) {
                BlockPos otherPos = getPartPosition(basePos, facing, i);
                if (otherPos != null) {
                    BlockState otherState = this.defaultBlockState()
                            .setValue(FACING, facing)
                            .setValue(getPartIdProperty(), i);

                    // 使用不同的更新标记以避免触发服务端逻辑
                    level.setBlock(otherPos, otherState, 2);
                }
            }
        }
    }

    /**
     * 方块放置后调用
     * 
     * @param level 世界对象
     * @param pos 位置
     * @param state 方块状态
     * @param placer 放置者
     * @param stack 使用的物品堆
     */
    @Override
    public void setPlacedBy(@NotNull Level level, @NotNull BlockPos pos, @NotNull BlockState state, @Nullable LivingEntity placer, @NotNull ItemStack stack) {
        super.setPlacedBy(level, pos, state, placer, stack);
        if (!level.isClientSide) {
            Direction facing = state.getValue(FACING);
            int partId = state.getValue(getPartIdProperty());

            // 放置其他部分
            placeOtherParts(level, pos, facing, partId);
        } else {
            // 在客户端清理预放置的部分并触发渲染更新
            cleanupAndRefresh(level, pos, state);
        }
    }

    /**
     * 客户端清理预放置部分并刷新渲染
     * 
     * @param level 世界对象
     * @param pos 位置
     * @param state 方块状态
     */
    private void cleanupAndRefresh(Level level, BlockPos pos, BlockState state) {
        if (level.isClientSide) {
            Direction facing = state.getValue(FACING);

            // 清理所有预放置的部分
            for (int i = 1; i < totalParts; i++) {
                BlockPos otherPos = getPartPosition(pos, facing, i);
                if (otherPos != null) {
                    // 检查预放置的部分是否仍然存在
                    BlockState otherState = level.getBlockState(otherPos);
                    if (otherState.getBlock() == this && otherState.getValue(getPartIdProperty()) == i) {
                        // 触发渲染更新而不是直接移除，让服务端响应来处理
                        level.sendBlockUpdated(otherPos, otherState, otherState, 3);
                    }
                }
            }

            // 更新主方块渲染
            level.sendBlockUpdated(pos, state, state, 3);
        }
    }

    // ==================== 更新形状 ====================

    /**
     * 更新形状
     * 当相邻方块更新时调用
     * 
     * @param state 当前方块状态
     * @param direction 更新方向
     * @param neighborState 相邻方块状态
     * @param level 世界对象
     * @param pos 当前位置
     * @param neighborPos 相邻位置
     * @return 更新后的方块状态
     */
    @Override
    @SuppressWarnings("deprecation")
    public @NotNull BlockState updateShape(BlockState state, @NotNull Direction direction, @NotNull BlockState neighborState, @NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockPos neighborPos) {
        Direction facing = state.getValue(FACING);
        int partId = state.getValue(getPartIdProperty());

        // 只有当邻居是相同类型的多方块部件时才进行检查
        if (neighborState.getBlock() == this) {
            // 计算预期的邻居位置
            BlockPos expectedNeighborPos = getExpectedNeighborPos(pos, facing, partId);

            // 只有当更新来自预期的邻居位置时才进行验证
            if (neighborPos.equals(expectedNeighborPos)) {
                // 检查相邻部分是否存在且匹配
                if (!isPartValid(level, pos, facing, partId)) {
                    return Blocks.AIR.defaultBlockState();
                }
            }
        }

        return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
    }

    /**
     * 计算预期的邻居位置
     * 子类可以覆盖此方法以支持复杂的结构
     * 
     * @param pos 当前位置
     * @param facing 朝向
     * @param partId 部分ID
     * @return 预期的邻居位置
     */
    protected BlockPos getExpectedNeighborPos(BlockPos pos, Direction facing, int partId) {
        // 默认实现只适用于简单的两部分结构
        if (partId == 0) {
            // 主部件的邻居在右侧
            return pos.relative(facing.getClockWise());
        } else {
            // 其他部件的邻居在左侧
            return pos.relative(facing.getCounterClockWise());
        }
    }

    /**
     * 获取指定部分的位置
     * 子类可以覆盖此方法以支持复杂的结构
     * 
     * @param basePos 基础位置
     * @param facing 朝向
     * @param partId 部分ID
     * @return 指定部分的位置
     */
    protected BlockPos getPartPosition(BlockPos basePos, Direction facing, int partId) {
        // 默认实现只适用于简单的两部分结构
        if (partId == 0) {
            return basePos;
        } else {
            // 默认将其他部分放在右侧
            return basePos.relative(facing.getClockWise());
        }
    }

    // ==================== 存活检查 ====================

    /**
     * 检查方块是否可以存活
     * 
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     * @return 是否可以存活
     */
    @Override
    @SuppressWarnings("deprecation")
    public boolean canSurvive(@NotNull BlockState state, @NotNull LevelReader level, @NotNull BlockPos pos) {
        return true;
    }

    // ==================== 移除逻辑 ====================

    /**
     * 方块被移除时调用
     * 
     * @param state 当前方块状态
     * @param level 世界对象
     * @param pos 位置
     * @param newState 新方块状态
     * @param isMoving 是否正在移动
     */
    @Override
    @SuppressWarnings("deprecation")
    public void onRemove(BlockState state, @NotNull Level level, @NotNull BlockPos pos, BlockState newState, boolean isMoving) {
        if (!state.is(newState.getBlock())) {
            // 只有主部分（PART_ID为0）负责处理掉落逻辑
            if (state.getValue(getPartIdProperty()) == 0) {
                dropItemsOnRemove(state, level, pos);
                // 只有主部分掉落方块本身
                popResource(level, pos, new ItemStack(this));
            }
            // 移除其他部分
            removeOtherParts(level, pos, state.getValue(FACING), state.getValue(getPartIdProperty()));
        }
        super.onRemove(state, level, pos, newState, isMoving);
    }

    // ==================== 流体状态 ====================

    /**
     * 获取流体状态
     * 
     * @param state 方块状态
     * @return 流体状态
     */
    @Override
    @SuppressWarnings("deprecation")
    public @NotNull FluidState getFluidState(@NotNull BlockState state) {
        return super.getFluidState(state);
    }

    // ==================== 碰撞与形状 ====================

    /**
     * 获取方块形状
     * 
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     * @param context 碰撞上下文
     * @return 方块形状
     */
    @Override
    @SuppressWarnings("deprecation")
    public @NotNull VoxelShape getShape(BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) {
        Direction facing = state.getValue(FACING);
        int partId = state.getValue(getPartIdProperty());
        
        ModelJsonVoxelShapeParser.CachedShapeGenerator generator = shapeGenerators.get(partId);
        if (generator != null) {
            return generator.getShape(facing);
        }
        
        return Shapes.block();
    }

    /**
     * 获取碰撞形状
     * 
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     * @param context 碰撞上下文
     * @return 碰撞形状
     */
    @Override
    @SuppressWarnings("deprecation")
    public @NotNull VoxelShape getCollisionShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull CollisionContext context) {
        return this.getShape(state, level, pos, context);
    }

    /**
     * 检查路径是否可通行
     * 
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     * @param type 路径计算类型
     * @return 路径是否可通行
     */
    @Override
    @SuppressWarnings("deprecation")
    public boolean isPathfindable(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull PathComputationType type) {
        return false;
    }
    
    // ==================== 公共方法 ====================

    /**
     * 获取总部分数
     * 
     * @return 总部分数
     */
    public int getTotalParts() {
        return totalParts;
    }
    
    /**
     * 获取特定部分的形状生成器
     * 
     * @param partId 部分ID
     * @return 形状生成器
     */
    public ModelJsonVoxelShapeParser.CachedShapeGenerator getShapeGenerator(int partId) {
        return shapeGenerators.get(partId);
    }

    /**
     * 获取方块的遮挡形状
     *
     * @param state 方块状态
     * @param level 世界对象
     * @param pos 位置
     * @return 遮挡形状
     */
    @Override
    @SuppressWarnings("deprecation")
    public @NotNull VoxelShape getOcclusionShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos) {
        // 不遮挡光线
        return Shapes.empty();
    }
}